added SSCLI 1.0
[windows-sources.git] / shared source / sscli20 / tools / nmake / exec.cpp
blobd91a3208fff7628cdfab1da5a916cac8c99e0303
1 // ==++==
2 //
3 //
4 // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
5 //
6 // The use and distribution terms for this software are contained in the file
7 // named license.txt, which can be found in the root of this distribution.
8 // By using this software in any fashion, you are agreeing to be bound by the
9 // terms of this license.
10 //
11 // You must not remove this notice, or any other, from this software.
12 //
14 // ==--==
15 // Exec.C - Contains routines that have do to with execing programs
17 // Purpose:
18 // Contains routines that spawn programs ...
20 #include "precomp.h"
21 #ifdef _MSC_VER
22 #pragma hdrstop
23 #endif
25 #define SLASH '\\'
26 #define PUBLIC
27 #define QUOTE '\"'
29 extern BOOL processInline(char *, char **, STRINGLIST **, BOOL);
31 #ifdef _M_IX86
32 extern UCHAR fRunningUnderChicago;
33 #else
34 #define fRunningUnderChicago FALSE
35 #endif
37 char * getComSpec(void);
38 BOOL iterateCommand(char*, STRINGLIST*, UCHAR, UCHAR, char *, unsigned*);
39 void removeQuotes(int, char **);
40 void touch(char*, BOOL);
41 void buildArgumentVector(unsigned int *, char **, char *);
44 //buffer for path of .cmd/.bat
45 extern char * makeStr;
46 extern char * shellName;
49 char szCmdLineBuf[MAXCMDLINELENGTH];
50 char *szNmakeProgName;
52 // buildArgumentVector -- builds an argument vector from a command line
54 // Scope:
55 // Local.
57 // Purpose:
58 // It builds an argument vector for a command line. This argument vector can
59 // be used by spawnvX routines. The algorithm is explained in the notes below.
61 // Input:
62 // argc -- The number of arguments created in the argument vector
63 // argv -- The actual argument vector created
64 // (Ignored if NULL)
65 // cmdline -- The command line whose vector is required
67 // Output:
68 // Returns the number of arguments and the argument vector as parameters
70 // Errors/Warnings:
71 // Assumes:
72 // That the behaviour of cmd.exe i.e. parses quotes but does not disturb them.
73 // Assumes that the SpawnVX routines will handle quotes as well as escaped
74 // chars.
76 // Modifies Globals:
77 // Uses Globals:
78 // Notes:
79 // Scan the cmdline from left to the end building the argument vector along
80 // the way. Whitespace delimits arguments except for the first argument for
81 // which the switch char '/' is also allowed. Backslash can be used to escape
82 // a char and so ignore the character following it. Parse the quotes along
83 // the way. If an argument begins with a double-quote then all characters till
84 // an unescaped double-quote are part of that argument. Likewise, if an
85 // unescaped Doublequote occurs within an argument then the above follows. If
86 // the end of the command line comes before the closing quote then the
87 // argument goes as far as that.
89 void
90 buildArgumentVector(
91 unsigned int *argc,
92 char **argv,
93 char *cmdline
96 char *p; // current loc in cmdline
97 char *end; // end of command line
98 #if PLATFORM_UNIX
99 // This determines if a '/' is accepted as a terminator for
100 // the first argument, as in the "dir/s" shorthand for "dir /s".
101 // That's never true on Unix.
102 BOOL fFirstTime = FALSE;
103 #else // PLATFORM_UNIX
104 BOOL fFirstTime = TRUE; // true if 1st argument
105 #endif // PLATFORM_UNIX
107 end = p = cmdline;
108 while (*end)
109 end++;
111 for (*argc = 0; p < end; ++*argc) {
112 p += _tcsspn(p, " \t"); // skip whitespace
113 if (p >= end)
114 break;
115 if (argv)
116 *argv++ = p;
117 if (*p == '\"') {
119 // If the word begins with double-quote, find the next
120 // occurrence of double-quote which is not preceded by backslash
121 // (same escape as C runtime), or end of string, whichever is
122 // first. From there, find the next whitespace character.
124 for (++p; p < end; p = _tcsinc(p)) {
125 if (*p == '\\')
126 ++p; // skip escaped character
127 else if (*p == '\"')
128 break;
130 if (p >= end)
131 continue;
132 ++p;
133 p = _tcspbrk(p, " \t");
134 } else {
136 // For the first word on the command line, accept the switch
137 // character and whitespace as terminators. Otherwise, just
138 // whitespace.
140 p = _tcspbrk(p, " \t\"/");
141 for (;p && p < end;p = _tcspbrk(p+1, " \t\"/")) {
142 if (*p == '/' && !fFirstTime)
143 continue; // after 1st word '/' is !terminator
144 else break;
146 if (p && *p == '\"') {
147 for (p++;p < end;p++) { // inside quote so skip to next one
148 if (*p == '\"')
149 break;
151 p = _tcspbrk(p, " \t"); // after quote go to first whitespace
153 if (fFirstTime) {
154 fFirstTime = FALSE;
156 // If switch char terminates the word, replace it with 0,
157 // re-allocate the word on the heap, restore the switch and set
158 // p just before the switch. It would be easier to shift
159 // everything right but then we have to worry about overflow.
161 if (p && *p == '/' && argv) {
162 *p = '\0';
163 argv[-1] = makeString(argv[-1]);
164 *p-- = '/';
168 if (!p)
169 p = end;
170 // Now, p points to end of command line argument
171 if (argv)
172 *p++ = '\0';
174 if (argv)
175 *argv = NULL;
178 PUBLIC int
179 doCommands(
180 char *name,
181 STRINGLIST *s,
182 STRINGLIST *t,
183 UCHAR buildFlags,
184 char *pFirstDep
187 STRINGLIST *temp;
188 int rc;
189 temp = makeNewStrListElement();
190 temp->text = makeString(name);
191 rc = doCommandsEx (temp, s, t, buildFlags, pFirstDep);
192 free_stringlist(temp);
193 return rc;
196 PUBLIC int
197 doCommandsEx(
198 STRINGLIST *nameList,
199 STRINGLIST *s,
200 STRINGLIST *t,
201 UCHAR buildFlags,
202 char *pFirstDep
205 char *u, *v;
206 UCHAR cFlags;
207 unsigned status = 0;
208 char c;
209 char *Cmd;
210 char *pLine;
211 BOOL fExpanded;
212 char *pCmd;
213 size_t cbLine;
215 #ifdef DEBUG_ALL
216 if (fDebug) {
217 printf("* doCommands:");
218 DumpList(nameList);
219 DumpList(s);
220 DumpList(t);
222 #endif
224 #ifdef DEBUG_ALL
225 printf("DEBUG: doCommands 1\n");
226 #endif
227 ++numCommands;
228 if (ON(gFlags, F1_QUESTION_STATUS))
229 return(0);
231 if (ON(gFlags, F1_TOUCH_TARGETS)) {
232 STRINGLIST *pName;
233 for (pName = nameList; pName; pName = pName->next) {
234 touch(pName->text, (USHORT) ON(buildFlags, F2_NO_EXECUTE));
236 return(0);
239 #ifdef DEBUG_ALL
240 printf("DEBUG: doCommands 2\n");
241 #endif
243 for (; s; s = s->next) {
244 fExpanded = processInline(s->text, &Cmd, &t,
245 ON(buildFlags, F2_DUMP_INLINE));
246 cFlags = 0;
247 errorLevel = 0;
248 u = Cmd;
249 for (v = u; *v; v = _tcsinc(v)) {
250 if (*v == ESCH) ++v;
251 else if (*v == '$') {
252 if (*++v == '$')
253 continue;
256 #ifdef DEBUG_ALL
257 printf("DEBUG: doCommands 2.1\n");
258 #endif
259 for (c = *u; c == '!' ||
260 c == '-' ||
261 c == '@' ||
262 c == ESCH ||
263 WHITESPACE(c); u = _tcsinc(u), c = *u) {
264 switch (c) {
265 case ESCH:
266 if (c = *++u, WHITESPACE(c))
267 c = ' '; // keep going
268 else
269 c = ESCH;
270 break;
272 case '!':
273 SET(cFlags, C_ITERATE);
274 break;
276 case '-':
277 SET(cFlags, C_IGNORE);
278 ++u;
279 if (_istdigit(*u)) {
280 char *pNumber = u;
282 errorLevel = _tcstoul(u, &u, 10);
283 if (errno == ERANGE) {
284 *u = '\0';
285 makeError(line, CONST_TOO_BIG, pNumber);
287 while(_istspace(*u))
288 u++;
289 } else
290 errorLevel = UINT_MAX;
291 --u;
292 break;
293 case '@':
294 if (
295 OFF(flags, F2_NO_EXECUTE)) {
296 SET(cFlags, C_SILENT);
298 break;
300 if (c == ESCH)
301 break; // stop parsing for cmd-line options
303 #ifdef DEBUG_ALL
304 printf("DEBUG: doCommands 2.2\n");
305 #endif
306 if (ON(cFlags, C_ITERATE) &&
307 iterateCommand(u, t, buildFlags, cFlags, pFirstDep, &status)
309 // The macros used by the command have to be freed & so we do so
311 v = u;
313 #ifdef DEBUG_ALL
314 printf("DEBUG: doCommands 2.21\n");
315 #endif
316 if (_tcschr(u, '$'))
317 u = expandMacros(u, &t);
319 #ifdef DEBUG_ALL
320 printf("DEBUG: doCommands 2.22\n");
321 #endif
322 if (v != u)
323 FREE(u);
324 if (OFF(buildFlags, F2_IGNORE_EXIT_CODES) &&
325 fOptionK &&
326 status &&
327 status > errorLevel)
329 break;
331 continue;
333 v = u;
335 #ifdef DEBUG_ALL
336 printf("DEBUG: doCommands 2.23\n");
337 #endif
338 if (!fExpanded && _tcschr(u, '$'))
339 u = expandMacros(u, &t);
341 #ifdef DEBUG_ALL
342 printf("DEBUG: doCommands 2.24\n");
343 #endif
345 cbLine = _tcslen(u) + 1;
346 pLine = (char *) rallocate (__max(cbLine, MAXCMDLINELENGTH));
347 _tcscpy(pLine, u);
349 // by this time $< has already been expanded.
350 // in order to allow processing of long commands that are due to
351 // batch-mode rules, use a buffer that may be larger than MAXCMDLINELENGTH
352 // Later we'll attempt to execute the long command directly, instead of
353 // passing it to the shell.
354 // Note: the macros expanded by ZFormat are not normally found in the
355 // command block of a batch-mode rule, so it should be safe to use
356 // max(cbLine, MAXCMDLINELENGTH) as a limit for ZFormat
357 if (ZFormat (pLine, __max(cbLine, MAXCMDLINELENGTH), u, pFirstDep))
358 makeError(0, COMMAND_TOO_LONG, u);
360 status = execLine(pLine,
361 (BOOL)(ON(buildFlags, F2_NO_EXECUTE)
362 || (OFF(buildFlags,F2_NO_ECHO)
363 && OFF(cFlags,C_SILENT))),
364 (BOOL)((OFF(buildFlags, F2_NO_EXECUTE)
366 || ON(cFlags, C_EXECUTE)),
367 (BOOL)ON(cFlags, C_IGNORE), &pCmd);
368 if (OFF(buildFlags, F2_IGNORE_EXIT_CODES)) {
369 if (status && status > errorLevel) {
370 if (!fOptionK)
371 makeError(0, BAD_RETURN_CODE, pCmd, status);
374 if (v != u)
375 FREE(u);
376 FREE(Cmd);
377 FREE(pLine);
378 if (OFF(buildFlags, F2_IGNORE_EXIT_CODES) &&
379 fOptionK &&
380 status &&
381 status > errorLevel)
383 break;
387 #ifdef DEBUG_ALL
388 printf("DEBUG: doCommands 3\n");
389 #endif
391 if (OFF(buildFlags, F2_IGNORE_EXIT_CODES) && fOptionK &&
392 (status > errorLevel))
393 return(status);
394 else
395 return(0);
399 // expandCommandLine -- expands %name% strings in the Command Line
401 // Purpose:
402 // The function expands '%name%' type strings in the Command Line. Its main
403 // job is to assist FEmulateCommand() in emulating set for OS/2.
405 // Modifies: buf -- The Command Line available globally
407 // Output:
408 // Returns -- the position of 'name=value' part in the Command Line.
409 // -- Null when no '=' is found so that FEmulateCommand() can pass the
410 // line to the shell to signal syntax error.
411 // Note:
412 // The shell does not give a syntax error for unmatched '%' and assumes it
413 // as just another character in this case. This behaviour is duplicated
414 // by expandCommandLine()
416 char *
417 expandCommandLine(
418 void
421 char Buf[MAXCMDLINELENGTH]; // Buffer for expanded string
422 char *pBuf;
423 char EnvBuf[MAXCMDLINELENGTH]; // getenv returned string copy
424 char *posName, // position of 'name=string' in Buf or buf
425 *p, // points into buf
426 *pEnv; // points into Env
427 char ExpandName[MAXNAME]; // %name% string
428 char *pExpandName;
430 pBuf = Buf;
431 _tcscpy(pBuf, "set");
432 p = szCmdLineBuf + 3; // go beyond 'set'
433 pBuf +=3;
434 /* Skip whitespace */
435 for (;;p++) {
436 if (!(WHITESPACE(*p)))
437 break; // argc>1 ð this will happen
438 else *pBuf++ = *p;
441 if (!_tcschr(p, '='))
442 return(""); // Syntax error so pass to the shell
443 else
444 posName = pBuf; // fixes position of Name in Buf
446 // Now we look for environment variables and expand if required
447 for (;*p != '=';p++)
448 *pBuf++ = *p;
450 for (;*p;) {
451 if (*p == '%') {
452 pExpandName = &ExpandName[0];
453 while (*++p != '%' && *p)
454 *pExpandName++ = *p;
455 *pExpandName = '\0';
456 if (!*p++) { // unmatched %;so don't expand
457 *pBuf='\0'; // from the environment; like set
458 _tcscat(Buf, ExpandName);
459 pBuf += _tcslen(ExpandName);
460 break; // Done precessing quit
461 } else { // matched %;so expand from the environment
462 EnvBuf[0] = '\0';
463 if ((pEnv = getenv(ExpandName)) != (char *)NULL) {
464 *pBuf='\0';
466 // If the expanded command line is too long
467 // just say that we can't expand it!!! #43290
468 size_t len = _tcslen(pEnv) + _tcslen(Buf);
469 if (len > MAXCMDLINELENGTH)
470 return NULL;
472 _tcscat(EnvBuf, pEnv);
473 _tcscat(Buf,EnvBuf);
474 pBuf += _tcslen(EnvBuf);
477 } else
478 *pBuf++ = *p++;
480 *pBuf = '\0';
481 _tcscpy(szCmdLineBuf, Buf);
482 *posName = '\0';
483 posName = szCmdLineBuf + _tcslen(Buf); // Offset into buf
484 return(posName);
487 // expandEnvVars -- expands %name% strings in szArg
489 // Returns -- szNew: the resulting expanded string
490 // (szNew should be FREEd by the caller)
492 char *
493 expandEnvVars(
494 char *szArg
497 char *pchLeft = NULL;
498 char *pchRight = NULL;
499 char *pchStart = szArg;
501 char *szNew = makeString("");
503 while (*pchStart) {
504 pchLeft = _tcschr(pchStart, '%');
505 if (pchLeft) {
506 pchRight = _tcschr(pchLeft + 1, '%');
509 if (pchLeft && pchRight) {
510 char *szEnv;
511 *pchLeft = '\0';
512 *pchRight = '\0';
513 szNew = reallocString(szNew, pchStart);
514 if ((szEnv = getenv(pchLeft + 1))) {
515 szNew = reallocString(szNew, szEnv);
517 else {
518 // no matching env var was found
519 // append the %..% string literary
520 *pchLeft = '%';
521 szNew = reallocString(szNew, pchLeft);
522 szNew = reallocString(szNew, "%");
524 *pchLeft = '%';
525 *pchRight = '%';
526 pchStart = pchRight + 1;
527 pchLeft = NULL;
528 pchRight = NULL;
530 else {
531 szNew = reallocString(szNew, pchStart);
532 pchStart += _tcslen(pchStart);
535 return szNew;
539 // FEmulateCommand - look for certain commands and emulate them
541 // Emulate $(MAKE), cd, chdir, and <drive letter>:.
542 // Also emulates 'set'.
544 // RETURNS: TRUE if command emulated, FALSE if not.
546 // Note:
547 // In set emulation if a syntax error is discovered then it lets the
548 // shell handle it. It does this by returning FALSE.
550 BOOL
551 FEmulateCommand(
552 int argc,
553 char **argv,
554 int *pStatus
557 char *pArg0 = argv[0];
558 char *pArg1 = argv[1];
560 #ifndef PLATFORM_UNIX
561 if (_istalpha(*pArg0) && pArg0[1] == ':' && !pArg0[2]) {
562 // If "<drive letter>:" then change drives. Ignore everything after
563 // the drive letter, just like the shell does.
564 char drive[3];
565 drive[0] = _totupper(*pArg0);
566 drive[1] = ':';
567 drive[2] = '\0';
568 SetCurrentDirectory(drive);
569 *pStatus = 0;
570 return(TRUE);
572 #endif //!PLATFORM_UNIX
574 if (!_tcsicmp(pArg0, "set")) {
575 char *pNameVal; // the "name=value" string
577 // If "set" then pass it to the shell and if "set string" then put it
578 // into the environment. Let the shell handle the syntax errors.
580 if (argc == 1) {
581 return(FALSE); // pass it to the shell
584 // expandCommandLine cannot handle lines > MAXCMDLINELENGTH
585 // In that case szCmdLineBuf will be empty
586 if (!szCmdLineBuf[0])
587 return (FALSE);
589 pNameVal = expandCommandLine();
591 if (pNameVal == NULL)
593 // Expanded commad line too long
594 return FALSE;
597 if (!*pNameVal) {
598 // If there is a syntax error let the shell handle it
600 return(FALSE);
603 if ((*pStatus = PutEnv(makeString(pNameVal))) == -1) {
604 makeError(currentLine, OUT_OF_ENV_SPACE);
606 } else {
607 // If "cd foo" or "chdir foo", do a chdir() else in protect mode this
608 // would be a no-op. Ignore everything after 1st arg, just like the
609 // shell does.
611 char *szArg;
613 if (!_tcsnicmp(pArg0, "cd", 2)) {
614 pArg0 += 2;
615 } else if (!_tcsnicmp(pArg0, "chdir", 5)) {
616 pArg0 += 5;
617 } else {
618 return(FALSE);
621 // At this point, a prefix of argv[0] matches cd or chdir and pArg0
622 // points to the next char. Check for a path separator in argv[0]
623 // (e.g., cd..\foo) or else use the next arg if present.
625 // if there are more than two arguments then let the shell handle it
626 if (argc > 2) {
627 return(FALSE);
630 // Remove quotes, if any from the argument
631 removeQuotes(argc, argv);
633 if (!*pArg0 && pArg1) {
634 // Under certain circumstances the C RunTime does not help us
635 // e.g. 'd:', in this case let the shell do it ...
636 if (isalpha(*pArg1) && pArg1[1] == ':' && !pArg1[2]) {
637 return(FALSE);
640 szArg = expandEnvVars(pArg1);
641 *pStatus = _chdir(szArg);
642 FREE (szArg);
643 } else if (*pArg0 == '.' || IsPathSeparator(*pArg0)) {
644 szArg = expandEnvVars(pArg0);
645 *pStatus = _chdir(szArg);
646 FREE (szArg);
647 } else {
648 // Unrecognized syntax--we can't emulate.
650 return(FALSE);
654 // If error, simulate a return code of 1.
656 if (*pStatus != 0) {
657 *pStatus = 1;
660 return(TRUE);
665 // execLine -- execute a command line
667 // Scope: Global (build.c, rpn.c)
669 // Purpose:
670 // Parses the command line for redirection characters and redirects stdin and
671 // stdout if "<", ">", or ">>" are seen. If any of the following occur,
672 // restore the original stdin and stdout, pass the command to the shell, and
673 // invoke the shell:
674 // - the command line contains "|" (pipe)
675 // - a syntax error occurs in parsing the command line
676 // - an error occurs in redirection
677 // Otherwise, attempt to invoke the command directly, then restore the
678 // original stdin and stdout. If this invocation failed because of
679 // file-not-found then pass the command to the shell and invoke the shell.
681 // Input: line -- The command line to be executed
682 // echoCmd -- determines if the command line is to be echoed
683 // doCmd -- determines if the command is to be actually executed
684 // ignoreReturn -- determines if NMAKE is to ignore the return code on
685 // execution
686 // ppCmd -- if non-null then on error returns command executed
688 // Output: Returns ... return code from child process
689 // ... -1 if error occurs
691 // Notes:
692 // 1/ Quoted strings can have redir chars "<>" which will be skipped over.
693 // 2/ Unmatched quotes cause error; redir chars are replaced by space char.
694 // 3/ Dup stdin file handle then redirect it. If we have to use the shell,
695 // restore the original command line.
696 // 4/ Emulate certain commands such as "cd" to help prevent some makefiles
697 // from breaking when ported from DOS to OS/2.
699 // Algorithm for spawning commands:
700 // If we can't handle the syntax, let the shell do everything. Otherwise,
701 // first check to see if the command (without extension) is a DOS built-in &
702 // if it is, call the shell to execute it (this is how cmd.exe behaves)
703 // If it's not a built-in, we check to see if it has a .cmd or a .bat
704 // extension (depending on whether we're in DOS or OS/2). If it does, we
705 // call system() to execute it.
706 // If it has some other extension, we ignore the extension and go looking for
707 // a .cmd or .bat file. If we find it, we execute it with system().
708 // Otherwise, we try to spawn it (without extension). If the spawn fails,
709 // we issue an unknown program error.
712 execLine(
713 char *line,
714 BOOL echoCmd,
715 BOOL doCmd,
716 BOOL ignoreReturn,
717 char **ppCmd
720 char **argv;
721 BOOL fUseShell;
722 BOOL fLongCommand;
723 int status;
724 unsigned int argc;
725 char *pCmdLineCopy;
727 if (!shellName) {
728 shellName = getComSpec();
731 switch (*line) {
732 case '@':
733 // Turn off echo if it was on. This handles the case where the "@"
734 // was in a macro.
736 line++;
737 if (doCmd)
738 echoCmd = 0;
739 break;
741 case '-':
742 ignoreReturn = TRUE;
743 ++line;
744 if (_istdigit(*line)) {
745 char * pNumber = line;
746 errorLevel = _tcstoul(line, &line, 10);
747 if (errno == ERANGE) {
748 *line = '\0';
749 makeError(0, CONST_TOO_BIG, pNumber);
751 while(_istspace(*line))
752 line++;
753 } else
754 errorLevel = UINT_MAX;
755 break;
758 // handle null command ...
759 if (!line[0])
760 return(0);
763 fLongCommand = _tcslen(line) >= MAXSHELLCMDLINELENGTH;
764 if (!fLongCommand)
765 _tcscpy(szCmdLineBuf, line);
766 else
767 *szCmdLineBuf = '\0';
769 // Allocate a copy of the command line on the heap because in a
770 // recursive call to doMake(), argv pointers will be allocated from
771 // the static buffer which will then be trashed. For buildArg...().
773 pCmdLineCopy = makeString(line);
775 #if PLATFORM_UNIX
776 // Replace any path separators with the native path separator.
777 char *tmp = pCmdLineCopy;
778 while((tmp = FindFirstPathSeparator(tmp))) {
779 *tmp++ = PATH_SEPARATOR_CHAR;
781 #endif // PLATFORM_UNIX
783 // If -n then echo command if not '$(MAKE)'
784 if (echoCmd) {
785 printf("\t%s\n", pCmdLineCopy);
786 fflush(stdout);
789 // Build arg vector. This is a waste on Windows NT since we're probably
790 // going to use the shell, except we have to check for cd, $(MAKE),
791 // etc. so we take advantage of the parsing code.
793 buildArgumentVector(&argc, NULL, pCmdLineCopy);
795 if (argc == 0) {
796 return(0); // for case when macro command is null
799 // allocate argv. Leave space for extra arguments
800 // (like "cmd", "/k", quotes) that may be added later
801 argv = (char **) rallocate((argc + 5) * sizeof (char *));
802 buildArgumentVector(&argc, argv, pCmdLineCopy);
804 // The _mbsicmp() does not like NULL pointer
805 // so I have to check before calling it.
806 if (argv[0] && makeStr && !_tcsicmp(argv[0], makeStr)) {
807 if(!szNmakeProgName) {
808 szNmakeProgName = _pgmptr;
809 if( _tcspbrk( szNmakeProgName," " )) { // If the program name has an embedded space in it
810 // Let's put quotes around it
811 szNmakeProgName = (char *)rallocate(_tcslen(szNmakeProgName)+3);
812 *szNmakeProgName = QUOTE; // First quote
813 *(szNmakeProgName+1) = '\0';
814 _tcscat( szNmakeProgName, _pgmptr ); // copy the full program name (self)
815 _tcscat( szNmakeProgName, "\""); // Final quote and \0
818 argv[0]=szNmakeProgName;
821 if (!doCmd) { // don't execute command if doCmd false
822 // For -n, emulate if possible.
824 if (FEmulateCommand(argc, argv, &status)) {
825 if (status && ppCmd) {
826 *ppCmd = makeString(*argv);
829 return(status); // return status
832 return(0);
835 // Try emulating the command if appropriate. If not, and we should not
836 // use the shell, try spawning command directly.
838 // Check status when emulating
840 if (FEmulateCommand(argc, argv, &status)) {
841 // Command has been emulated. Don't execute it again.
843 fUseShell = FALSE;
845 } else {
846 fUseShell = TRUE;
849 if (fUseShell) {
850 int i;
851 #if PLATFORM_UNIX
852 BOOL fExtraQuote = TRUE;
853 #else
854 BOOL fExtraQuote = FALSE;
856 // copy command line into buffer
857 if (_tcslen(line) < MAXCMDLINELENGTH)
858 _tcscpy(szCmdLineBuf, line);
859 else
860 makeError(0, COMMAND_TOO_LONG, line);
862 // IF argv[0] (before we rearrange with cmd.exe /c) is quoted AND
863 // any of the other argv[1...n] args have quotes AND
864 // running on NT
865 // THEN we add an extra quote before argv[0] and one after argv[n].
867 if ((*argv[0] == QUOTE) &&
868 (*(argv[0] + _tcslen(argv[0]) - 1) == QUOTE) &&
869 !fRunningUnderChicago) {
870 for (i = argc - 1; i >= 1; i--) {
871 if( _tcspbrk( argv[i],"\"" )) {
872 fExtraQuote = TRUE;
873 break;
877 #endif
879 if (fExtraQuote) {
880 argv[argc++] = "\"";
881 argv[argc] = NULL;
884 for (i = argc; i >= 0; i--) {
885 argv[i+2] = argv[i];
887 argv[0] = shellName;
888 #if PLATFORM_UNIX
889 argv[1] = fExtraQuote ? (char *)"-c \"" : (char *)"-c";
890 #else
891 argv[1] = fExtraQuote ? (char *)"/c \"" : (char *)"/c";
892 #endif
895 #if PLATFORM_UNIX
896 status = _spawnvp(P_WAIT, argv[0], (const char * const *)&argv[1]);
897 #else
898 status = _spawnvp(P_WAIT, argv[0], (const char * const *)argv);
899 #endif
903 // Check for errors spawning command (distinct from errors *returned*
904 // from a successfully spawned command).
906 if (status == -1) {
907 if (ignoreReturn) {
908 status = 0;
909 } else {
910 switch (errno) {
911 case 0:
912 // We (ie: nmake) didn't fail, but the spawned program did.
913 break;
915 case ENOENT:
916 makeError(0, CANT_FIND_PROGRAM, argv[0]);
917 break;
919 case ENOMEM:
920 makeError(0, EXEC_NO_MEM, fUseShell ? argv[2] : argv[0]);
921 break;
923 default:
924 // Done to flag possibly erroneous decision made here [SB]
925 makeError(0, SPAWN_FAILED_ERROR, _strerror(NULL));
930 if (status && ppCmd) {
931 *ppCmd = makeString(fUseShell ? argv[2] : argv[0]);
934 FREE(argv);
935 FREE(pCmdLineCopy);
936 return(status);
940 // getComSpec()
942 // actions: Attempts to find system shell.
944 // First look for COMSPEC. If not found, look for COMMAND.COM or CMD.EXE
945 // in the current directory then the path. If not found, fatal error.
946 // It would make sense to give an error if COMSPEC is not defined but
947 // test suites are easier if no user-defined environment variables are
948 // required.
950 char *
951 getComSpec()
953 char *szShell;
954 char szPath[_MAX_PATH];
956 if ((szShell = getenv("COMSPEC")) != NULL) {
957 return(szShell);
960 #if PLATFORM_UNIX
961 strcpy(szPath, "/bin/sh");
962 #else // PLATFORM_UNIX
963 szShell = (char *)"system32\\cmd.exe";
964 if (!GetEnvironmentVariable("systemroot",
965 szPath,
966 sizeof(szPath)-strlen(szShell))) {
967 makeError(0, NO_COMMAND_COM);
969 strcat(szPath, szShell);
970 #endif // PLATFORM_UNIX
972 return(makeString(szPath));
976 BOOL
977 iterateCommand(
978 char *u,
979 STRINGLIST *t,
980 UCHAR buildFlags,
981 UCHAR cFlags,
982 char *pFirstDep,
983 unsigned *status
986 BOOL parens;
987 char c = '\0';
988 char *v;
989 STRINGLIST *p = NULL,
991 char *pLine;
992 char *pCmd;
994 for (v = u; *v ; ++v) {
995 parens = FALSE;
996 if (*v == '$') {
997 if (*(v+1) == '(') {
998 ++v;
999 parens = TRUE;
1001 if (*(v+1) == '?') {
1002 if (parens
1003 && !(_tcschr("DFBR", *(v+2)) && *(v+3) == ')')
1004 && *(v+2) != ')')
1005 continue;
1006 p = dollarQuestion;
1007 c = '?';
1008 break;
1010 if (*++v == '*' && *(v+1) == '*') {
1011 if (parens
1012 && !(_tcschr("DFBR", *(v+2)) && *(v+3) == ')')
1013 && *(v+2) != ')')
1014 continue;
1015 p = dollarStarStar;
1016 c = '*';
1017 break;
1022 if (!*v) {
1023 return(FALSE);
1026 v = u;
1027 q = p;
1028 while (p) {
1029 macros = t;
1030 if (c == '*') {
1031 p = dollarStarStar->next;
1032 dollarStarStar->next = NULL;
1033 } else {
1034 p = dollarQuestion->next;
1035 dollarQuestion->next = NULL;
1037 u = expandMacros(v, &macros);
1039 expandExtmake(CmdLine, u, pFirstDep);
1040 pLine = CmdLine;
1041 *status = execLine(pLine,
1042 (BOOL)(ON(buildFlags, F2_NO_EXECUTE)
1043 || (OFF(buildFlags,F2_NO_ECHO)
1044 && OFF(cFlags,C_SILENT))),
1045 (BOOL)((OFF(buildFlags, F2_NO_EXECUTE)
1047 || ON(cFlags, C_EXECUTE)),
1048 (BOOL)ON(cFlags, C_IGNORE), &pCmd);
1049 if (OFF(buildFlags, F2_IGNORE_EXIT_CODES)) {
1050 if (*status && *status > errorLevel)
1051 if (!fOptionK)
1052 makeError(0, BAD_RETURN_CODE, pCmd, *status);
1055 if (c == '*')
1056 dollarStarStar = dollarStarStar->next = p;
1057 else
1058 dollarQuestion = dollarQuestion->next = p;
1059 FREE(u);
1060 if (OFF(buildFlags, F2_IGNORE_EXIT_CODES) &&
1061 fOptionK &&
1062 *status &&
1063 *status > errorLevel)
1065 break;
1068 if (c == '*')
1069 dollarStarStar = q;
1070 else
1071 dollarQuestion = q;
1072 return(TRUE);
1076 void
1077 removeQuotes(
1078 int argc,
1079 char **argv
1082 char *t,
1083 *string;
1085 for (; argc--; argv++) {
1086 string = *argv;
1087 for (t = string; *t;) {
1088 if (*t == SLASH || *t == ESCH) {
1089 if (t[1] == QUOTE)
1090 *(string)++ = *(t++);
1091 *(string++) = *(t++);
1092 continue;
1094 if (*t == QUOTE)
1095 ++t;
1096 else {
1097 if (_istlead(* (unsigned char *)t))
1098 *(string++) = *(t++);
1099 *(string++) = *(t++);
1102 *string = '\0';
1106 void
1107 touch(
1108 char *s,
1109 BOOL minusN
1112 // If name contains Quotes, remove these before opening the file
1114 if (*s == '"') {
1115 *(_tcsrchr(s, '"')) = '\0';
1116 _tcscpy(s, s+1);
1119 makeMessage(TOUCHING_TARGET, s);
1121 if (minusN) {
1122 return;
1125 FILETIME ft;
1127 GetSystemTimeAsFileTime(&ft);
1129 HANDLE hf = CreateFile(s,
1130 GENERIC_WRITE,
1132 NULL,
1133 OPEN_EXISTING,
1134 FILE_ATTRIBUTE_NORMAL,
1135 NULL);
1137 if (hf == INVALID_HANDLE_VALUE) {
1138 return;
1141 SetFileTime(hf, NULL, NULL, &ft);
1142 CloseHandle(hf);